#pragma once

#include <iostream>
#include <sys/select.h>
#include "Sock.hpp"

#define READ_EVENT 0x01
#define WRITE_EVENT (0x01 << 1)
#define EXCEPT_EVENT (0x01 << 2)

static const int N = sizeof(fd_set) * 8;

static const int default_fd = -1;

static const uint8_t default_event = 0;

typedef class FdData // 主要为了管理不同fd带不同的事件
{
public:
    int fd;
    uint8_t event;
    std::string clientIp;
    uint16_t clientPort;
} type_t;

class SelectServer
{
public:
    SelectServer(uint16_t serverPort)
        : _serverPort(serverPort)
    {
    }

    void Init()
    {
        _sock.Socket();
        _sock.Bind(_serverPort);
        _sock.Listen();

        for (int i = 0; i < N; ++i)
        {
            _FdArray[i].fd = default_socket;//clientIp是string类型，初始大小是0
            _FdArray[i].event = default_event;
            _FdArray[i].clientPort = 0;
        }
    }

    void Start()
    {
        _FdArray[0].fd = _sock.Fd();
        _FdArray[0].event = READ_EVENT;
        while (true)
        {
            fd_set ReadSet;//读的
            fd_set WriteSet;//写的
            FD_ZERO(&ReadSet);//清空
            FD_ZERO(&WriteSet);
            int MaxFd = _FdArray[0].fd;
            for(int i = 0; i < N; ++i)
            {
                if(_FdArray[i].event & READ_EVENT)//注意。这里不能用if else，因为可能有的fa两个事件都关心
                    FD_SET(_FdArray[i].fd,&ReadSet);//读事件
                
                if(_FdArray[i].event & WRITE_EVENT)//写事件
                    FD_SET(_FdArray[i].fd,&WriteSet);

                if(MaxFd < _FdArray[i].fd)//求最大fd
                    MaxFd = _FdArray[i].fd;
            }
            struct timeval timeout = {1,0};
            int n = select(MaxFd+1,&ReadSet,&WriteSet,nullptr,&timeout);
            if(n == 0)
                LogMessage(Debug,"timeout ...");
            else if(n < 0)
                LogMessage(Error,"select fail , %d,%s",errno,strerror(errno));
            else
            {
                std::cout << "有一个事件就绪" << std::endl;
                HandlerEvent(ReadSet,WriteSet);
            }
        }
    }

    void HandlerEvent(fd_set& ReadSet,fd_set& WriteSet)
    {
        for(int i = 0; i < N; ++i)
        {
            if(_FdArray[i].fd == default_fd)
                continue;
            
            if((_FdArray[i].event & READ_EVENT) && FD_ISSET(_FdArray[i].fd,&ReadSet))//读事件，并且就绪
            {
                if(_FdArray[i].fd == _sock.Fd())//连接就绪
                {
                    Accept();
                }
                else if(_FdArray[i].fd != _sock.Fd())//读就绪
                {
                    Recv(_FdArray[i].fd,i);
                }
            }
            else if((_FdArray[i].event & WRITE_EVENT) && FD_ISSET(_FdArray[i].fd,&WriteSet))//写事件就绪
            {
                //TODO
            }
            else//其他事件，比如异常事件
            {
                //TODO;
            }

        }
    }

    void Accept()
    {
        std::string clientIp;
        uint16_t clientPort;
        int sock = _sock.Accept(&clientIp,&clientPort);
        if(sock > 0)
            LogMessage(Debug,"%s-%d accept success",clientIp.c_str(),clientPort);
        int pos = 0;
        for(; pos < N; ++pos)
        {
            if(_FdArray[pos].fd == default_fd)
                break;
        }
        _FdArray[pos].fd = sock;
        //_FdArray[pos].event = READ_EVENT | WRITE_EVENT;//读写都关心
        _FdArray[pos].event = READ_EVENT;
        _FdArray[pos].clientIp = clientIp;
        _FdArray[pos].clientPort = clientPort;
    }

    void Recv(int& sock,int& i)
    {
        char buffer[1024];
        int n = recv(sock,buffer,sizeof(buffer),0);
        if(n == 0)
        {
            LogMessage(Debug,"%s-%d exit",_FdArray[i].clientIp.c_str(),_FdArray[i].clientPort);
            Close(i);
        }
        else if(n < 0)
        {
            LogMessage(Error,"recv fail,%d,%s",errno,strerror(errno));
            Close(i);
        }
        else
        {
            buffer[n] = '\0';
            std::cout << "[client send]# " << buffer << std::endl;
            std::string message = "[server send]# ";
            message += buffer;
            send(sock,message.c_str(),message.size(),0);
        }
    }

    void Close(int i)
    {
        close(_FdArray[i].fd);
        _FdArray[i].fd = default_fd;
        _FdArray[i].event = default_event;
        _FdArray[i].clientIp.resize(0);
        _FdArray[i].clientPort = 0;
    }

    ~SelectServer()
    {
        _sock.Close();
    }

private:
    Sock _sock;
    uint16_t _serverPort;
    type_t _FdArray[N];
};